﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Timers;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace DBQueryTools
{
    //委托
    public delegate bool DealMsgDelegate(object obj, byte[] bs, uint size, uint type);

    /// <summary>
    /// 使用SocketAsync 实现客户端链接的异步处理
    /// 解决在同步文件时，同步速度缓慢的情况
    /// </summary>
    public class AsyncClient
    {

        //定义接收缓冲区
        public CircleBuffer<byte> circlebuff = new CircleBuffer<byte>();
        public Socket sock;
        public string _ip; //链接IP
        public int _port; //链接端口
        public int _sta; //登录状态 0  未链接/未登录 1 登录成功
        public string _loginIP;
        public string _loginName;
        private DateTime dt; //运行时间
        public int count = 0;
        //
        public object token = null;

        //处理消息委托
        public DealMsgDelegate deal_msg_data;

        public AsyncClient(string loginIP,string loginName, DealMsgDelegate _deal, object _token)
        {
            _sta = 0;
			_loginIP = loginIP;
			_loginName = loginName;
			//
			deal_msg_data = _deal;
            token = _token;

            //记录操作时间
            dt = DateTime.Now;

            count = 0;
        }

        public void CloseConnet()
        {
            _sta = 0;
        }
        //更新连接信息
        public void updatestate(int val)
        {
            _sta = val;
        }

        public bool IsConnect()
        {
            DateTime now = DateTime.Now;
            TimeSpan span = now - dt;
            if (span.TotalSeconds > 10)
            {
                return false;
            }
            return true;
        }

        //异步链接组件
        private SocketAsyncEventArgs ConnectSAE = new SocketAsyncEventArgs();
        //开启连接
        public bool OpenScoekt(string ip, int port)
        {
            _ip = ip;
            _port = port;
            _sta = 0;
            sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                // 与目标终端连接.
                ConnectSAE.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(_ip), Convert.ToInt32(_port));
                ConnectSAE.Completed += new EventHandler<SocketAsyncEventArgs>(ConnectCallback);
                ConnectSAE.UserToken = this;
                //
                sock.ConnectAsync(ConnectSAE);

                //记录操作时间
                dt = DateTime.Now;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
            return true;
        }

        //连接成功，发送登录信息
        public bool Login()
        {
            //封包处理
            msgHead_Login msgLogin = new msgHead_Login();
            

            //_loginIP.CopyTo(0, msgLogin.szXXIP, 0, _loginIP.Length);
            //_loginName.CopyTo(0, msgLogin.szXXOOName, 0, _loginName.Length);
            
            msgLogin.szXXIP = _loginIP;
            msgLogin.szXXOOName = _loginName;
            msgLogin.dwVersion = Tools.msgHeadVersioin(0);
            msgLogin.dwCommand = 55003;
            msgLogin.dwSize = Convert.ToUInt32(Marshal.SizeOf(typeof(msgHead_Login)));

            byte[] bs = Tools.StructToBytes((object)msgLogin, Convert.ToInt32(Marshal.SizeOf(typeof(msgHead_Login))));
            //
            Send(bs, bs.Length);

            return true;
        }

        //发送数据
        public int Send(byte[] buffer, int length)
        {
            int ret = 0;
            if (sock.Connected)
            {
                SocketAsyncEventArgs SendSAE = new SocketAsyncEventArgs();
                //
                SendSAE.SetBuffer(buffer, SendSAE.Offset, length);
                // 初始化sendSAE
                SendSAE.Completed += new EventHandler<SocketAsyncEventArgs>(SendSAE_Completed);
                SendSAE.UserToken = this;
                //
                sock.SendAsync(SendSAE);

                //记录操作时间
                dt = DateTime.Now;

                ret += length;
            }
            return ret;
        }

        //链接回调
        private static void ConnectCallback(object sender, SocketAsyncEventArgs e)
        {
            Socket sock = sender as Socket;
            AsyncClient client = e.UserToken as AsyncClient;
            if (e.SocketError == SocketError.Success && client.sock.Connected)
            {
                SocketAsyncEventArgs RecieveSAE = new SocketAsyncEventArgs();
                byte[] buffer = new byte[102400];
                RecieveSAE.RemoteEndPoint = e.RemoteEndPoint;
                RecieveSAE.SetBuffer(buffer, 0, buffer.Length);
                RecieveSAE.UserToken = e.UserToken;
                RecieveSAE.Completed += new EventHandler<SocketAsyncEventArgs>(RecieveSAE_Completed);
                try
                {
                    if (client._sta == 0)
                    {
                        client.Login();
                    }
                    //设置接收回调
                    sock.ReceiveAsync(RecieveSAE);//ReceiveAsync(RecieveSAE);
                    //记录操作时间
                    client.dt = DateTime.Now;
                    //开启处理线程
                    //ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncClient.ThreadRecieveMsg), client);
                }
                catch (Exception m)
                {
                    Console.WriteLine(m.Message);
                }
                //clientSk.SendAsync(SendSAE);
            }
            else
            {
                Console.WriteLine("链接失败");
                //封包处理
                msgHead_Result msgret = new msgHead_Result();
                
                msgret.dwVersion = Tools.msgHeadVersioin(0);
                msgret.dwCommand = 55002;
                msgret.dwSize = Convert.ToUInt32(Marshal.SizeOf(typeof(msgHead_Result)));
                msgret.dwCode = 0;
                msgret.szErrInfo = e.SocketError.ToString();

                byte[] bs = Tools.StructToBytes((object)msgret, Convert.ToInt32(Marshal.SizeOf(typeof(msgHead_Result))));

                client.deal_msg_data(client.token, bs, Convert.ToUInt32(bs.Length), 55002);
            }
        }
        //接收回调
        private static void RecieveSAE_Completed(object sender, SocketAsyncEventArgs e)
        {
            AsyncClient client = e.UserToken as AsyncClient;
            Socket sock = sender as Socket;

            //client.count += 1;
            //Console.WriteLine("------------------------------  " + e.BytesTransferred);
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                byte[] data = e.Buffer;  //注意这里，如何取关联到套接字的发送接受的缓冲区中的值。
                //把数据写入缓冲区
                client.circlebuff.Write(data, 0, e.BytesTransferred);
                //
                client.RecieveDealMsg();
                //继续接收数据
                if (client._sta > 0)
                {
                    sock.ReceiveAsync(e);
                }
            }
            else
            {
                //sock.Close(10);
            }
        }

        //处理数据 
        public void RecieveDealMsg()
        {
            //分包处理
            int available = circlebuff.Available; //可读字节数
            byte[] buffer = new byte[2048];
            int headlen = Convert.ToInt32(Marshal.SizeOf(typeof(msgHead)));
            //
            while (available > 0)
            {
                available = circlebuff.Available; //可读字节数
                //Console.WriteLine("可读字节数 : " + available);
                if (available <= headlen)
                {
                    break;
                }
                int ret = circlebuff.Read(buffer, 0, headlen, false);
                if (ret != headlen)
                {
                    break;
                }
                //取消息头
                msgHead head = Tools.GetMsgHead(buffer);
                if (head.dwSize == 0 || head.dwSize > 10240)
                {
                    Console.WriteLine("丢掉4字节 " );
                    circlebuff.Read(buffer, 0, 4, true);
                    continue;
                }
                //收到字节数不够 
                if (head.dwSize > available)
                {
                    Console.WriteLine("收到字节数不够  ");
                    break;
                }

                //CRC校验
                //取数据
                ret = circlebuff.Read(buffer, 0, Convert.ToInt32(head.dwSize), true);
                if (ret != head.dwSize)
                {
                    break;
                }
                //处理一个完整的数据报文
                deal_msg_data(token, buffer, head.dwSize, head.dwCommand);
                //善后处理
                //记录操作时间
                dt = DateTime.Now;
                //available -= Convert.ToInt32(head.dwSize);
            }
        }

        //发送成功回调
        private static void SendSAE_Completed(object sender, SocketAsyncEventArgs e)
        {
            AsyncClient client = e.UserToken as AsyncClient;
            //记录操作时间
            client.dt = DateTime.Now;

            Socket sk = sender as Socket;
            if (e.SocketError == SocketError.Success)
            {
                Debug.WriteLine("Send " + e.Buffer.Length + " 字节");
            }
        }

        public Socket GetBlockSocket(int iport)
        {
            IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(_ip), iport);
            Socket tempSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            tempSocket.Connect(ipe);

            if (!tempSocket.Connected)
            {
                return null;
            }
            return tempSocket;
        }
        /*
        //处理数据线程
        public static void ThreadRecieveMsg(object obj)
        {
            AsyncClient client = obj as AsyncClient;
            while (true)
            {
                if (client.sock.Connected)
                {
                    client.RecieveDealMsg();
                }
                else
                {
                    Console.WriteLine("线程关闭");
                    break;
                }
                //睡眠200毫秒
                Thread.Sleep(1000);
                Console.WriteLine("=================================");
            }
        }
         * */
    }

}